use crate::error::Result;
use crate::manifest::Manifest;
use crate::services::*;
use crate::services::context_manager::ContextManager;
use crate::services::contribution_keyword_manager::{ContributionKeywordManager, IContributionManager};
use crate::services::event_manager::EventManager;
use crate::services::extension_manager::ExtensionManager;
use crate::services::manifest_manager::ManifestManager;
use crate::services::when_eval::ActivationContext;
use parking_lot::RwLock;
use std::collections::HashMap;
use std::sync::Arc;

pub struct ExpansionSystem {
    services: Arc<Services>,
}

pub struct Services {
    pub manifest_manager: RwLock<ManifestManager>,
    pub extension_manager: RwLock<ExtensionManager>,
    pub context_manager: RwLock<ContextManager>,
    pub contribution_keyword_manager: RwLock<ContributionKeywordManager>,
    pub event_manager: RwLock<EventManager>,
}

impl ExpansionSystem {
    pub fn new(packages: Vec<Box<dyn PluginPackage>>) -> Result<Self> {
        let mut implementations = HashMap::new();
        let mut manifests = Vec::new();

        for package in packages {
            let manifest: Manifest = serde_json::from_str(package.manifest_str())?;
            implementations.insert(manifest.name.clone(), package.implementation());
            manifests.push(manifest);
        }

        let services = Arc::new(Services {
            manifest_manager: RwLock::new(ManifestManager::new(manifests)?),
            extension_manager: RwLock::new(ExtensionManager::new(implementations)),
            context_manager: RwLock::new(ContextManager::new()),
            contribution_keyword_manager: RwLock::new(ContributionKeywordManager::new()),
            event_manager: RwLock::new(EventManager::new()),
        });

        services.contribution_keyword_manager.read().subscribe_to_events(&services.event_manager.read());

        Ok(Self { services })
    }

    pub fn activate(&self, event: &str, context: &ActivationContext) -> Result<()> {
        let manifest_manager = self.services.manifest_manager.read();
        self.services.extension_manager.write().activate_with_deps(
            event,
            &*manifest_manager,
            context,
            self.services.clone(),
        )
    }

    pub fn deactivate(&self, event: &str) -> Result<()> {
        let manifest_manager = self.services.manifest_manager.read();
        self.services.extension_manager.write().deactivate_with_deps(event, &*manifest_manager)
    }
}
